﻿using System;
using System.Linq;
using System.Collections.Generic;

using System.ServiceModel;
using System.ServiceModel.Description;
using CoreEx.Common.Extensions;
using CoreEx.DynamicInvoker;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
using LinFu.Proxy.Interfaces;
using CoreEx.Common.Proxy;
using CoreEx.Common.Logging;
namespace CoreEx.ServiceModel.Implementation
{
    /// <summary>
    /// A custom <see cref="ServiceHost"/> that allows <see cref="IServiceBehavior"/> 
    /// instances to be added to the <see cref="ServiceHostBase.Description"/> before 
    /// the host is opened.
    /// </summary>
    [Implements(typeof(ServiceHost))]
    public class ServiceContainerServiceHost : ServiceHost, IInitialize
    {
        private IServiceContainer _serviceContainer;

        private readonly Type _serviceType;

        private readonly Uri[] _baseAddresses;

        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceContainerServiceHost"/> class.
        /// </summary>
        /// <param name="serviceType">The type of service to host.</param>
        /// <param name="baseAddresses">The base addresses of the hosted service.</param>
        public ServiceContainerServiceHost(Type serviceType, params Uri[] baseAddresses) 
        {            
            this.LogDebug(string.Format("Initializing ServiceHost for service type : {0}",serviceType.Name));
            this.LogDebug(string.Format("BaseAddresses is {0}",baseAddresses.Select(a => a.ToString()).Aggregate((next,current) => next + ", " + current)));
            _serviceType = serviceType;
            _baseAddresses = baseAddresses;            
        }


        protected IBindingProvider BindingProvider { get; set; }


        /// <summary>
        /// Initializes this service instance.
        /// </summary>        
        public void Initialize(IServiceContainer source)
        {
            _serviceContainer = source;
            var serviceProxyType = CreateServiceProxyType();
            BindingProvider = source.GetService<IBindingProvider>();    
            InitializeDescription(serviceProxyType ,new UriSchemeKeyedCollection(_baseAddresses));
            Description.Name = _serviceType.Name;
            AddServiceEndPoints();                        
            InitializeService(source);            
        }


        



        private void AddServiceEndPoints()
        {
            foreach (var baseAddress in _baseAddresses)
            {
                var binding = BindingProvider.CreateBinding(_serviceType, baseAddress);
                AddServiceEndpoint(_serviceType, binding, baseAddress);
            }
        }

        private Type CreateServiceProxyType()
        {
            var proxyFactory = _serviceContainer.GetService<IProxyFactory>();
            var serviceProxyType = proxyFactory.CreateProxyType(_serviceType, Type.EmptyTypes);

            var constructorInterceptor =
                _serviceContainer.GetService<IConstructorInterceptor>("ServiceContractConstructorInterceptor",
                                                                      _serviceType);

            //Assign the IConstructorInterceptor
            serviceProxyType.DynamicInvoke("ConstructorInterceptor", constructorInterceptor);

            return serviceProxyType;
        }

        private void InitializeService(IServiceContainer serviceContainer)
        {
            var serviceInitializers =  serviceContainer.GetServices<IInitializer<ServiceHost>>().ToList();
            serviceInitializers.ForEach(si => si.Initialize(this));            
        }

    }
}